package org.bigjava.novelplatform.common.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;

/**
 * @author ykh
 * @create 2022-04-24 7:34
 */
@Slf4j
@Configuration
@EnableRabbit
public class Config implements InitializingBean{

    /**
     * 无需手动序列化
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 创建JSON序列化工具
        RedisSerializer jsonRedisSerializer =
                springSessionDefaultRedisSerializer();
        // 设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置Value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        // 返回
        return template;
    }

    //为spring-session-redis设置
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

//    @Bean
//    public ValueOperations<String, String> ops(StringRedisTemplate redisTemplate){
//        return redisTemplate.opsForValue();
//    }

    //JacksonAutoConfiguration
    @Autowired
    ObjectMapper objectMapper;

    //在所有属性都注入后设置
    @Override
    public void afterPropertiesSet() throws Exception {
        //spring.jackson.deserialization.READ_UNKNOWN_ENUM_VALUES_AS_NULL:true
        //spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES:false

        //允许出现无法解析enum值
        objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
                //忽略未知字段，而不抛出异常
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    //objectMapper也可以这样配置
//    @Bean
//    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(ApplicationContext applicationContext,
//                                                                                       JacksonProperties jacksonProperties) {
//        return new Jackson2ObjectMapperBuilderCustomizer() {
//            @Override
//            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
//
//            }
//        };
//    }

    @Bean
    public MessageConverter messageConverter(){
        //Java 8 date/time type `java.time.LocalTime` not supported by default:
        // add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
        //不要直接使用rabbitTemplate.setMessageConverter();
        return new Jackson2JsonMessageConverter();
    }

    @Configuration
    protected static class WebsocketConfig implements InitializingBean{

        @Override
        public void afterPropertiesSet() throws Exception {

        }

        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }

    }

    @Configuration
    protected static class RabbitmqConfig implements InitializingBean{
        @Resource
        RabbitTemplate rabbitTemplate;

        @Bean("delayed_exchange")
        public CustomExchange delayedExchange(){
            HashMap<String, Object> arg = new HashMap<>();
//        插件设置交换机类型：直接(direct), 主题(topic) ,标题(headers) , 扇出(fanout)
            arg.put("x-delayed-type", "direct");
            return new CustomExchange("delayed_exchange", "x-delayed-message", true, false, arg );
        }

        @Bean("delayed_onlyOneQueue")
        public Queue onlyOneQueue() {
            return QueueBuilder.durable("delayed_onlyOneQueue").build();
        }

        @Bean("delayed_continuedQueue")
        public Queue continuedQueue() {
            return QueueBuilder.durable("delayed_continuedQueue").build();
        }

        @Bean
        public Binding delayedOnlyOneBinding(@Autowired @Qualifier("delayed_onlyOneQueue") Queue queue,
                                             @Autowired @Qualifier("delayed_exchange") CustomExchange exchange
        ){
            return BindingBuilder.bind(queue).to(exchange).with("onlyOne").noargs();
        }

        @Bean
        public Binding delayedContinuedBinding(@Autowired @Qualifier("delayed_continuedQueue") Queue queue,
                                               @Autowired @Qualifier("delayed_exchange") CustomExchange exchange
        ){
            return BindingBuilder.bind(queue).to(exchange).with("continued").noargs();
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            //下一条由yml配置替代spring.rabbitmq.template.mandatory: true
//        rabbitTemplate.setMandatory(true);

            //生产者投递消息到交换失败回调
            //开启确认模式publisher-returns： true
            rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
                //CorrelationData在消息发送时传入
                if (ack) {
                    log.info("唯一id为{}的消息,已抵达交换机", correlationData.getId());
                } else {
                    log.warn("唯一id为{}的消息,未抵达交换机.原因：{}", correlationData.getId(), cause);
                }
            });
            //交换机投递到队列失败回调
            //开启消息投递到队列失败的回调publisher-returns: true
            rabbitTemplate.setReturnsCallback((returned) -> {
                //由于使用了延迟插件，延迟由路由管理，消息不会第一时间发往队列，进而导致当前回调一定会被触发
                log.warn("消息：{}未成功抵达队列,原因：{},路由key：{}", returned.getMessage(), returned.getReplyText(),
                        returned.getRoutingKey());
            });
        }


    }


}
